﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using EnvDTE80;

namespace CSharpCodeAssistant
{
    partial class SelectMembersForm : Form
    {
        ListOfOptionsForFirstWindow optionSelected;
        CodeGenerator codeGenerator;
        /// <summary>
        /// This variable is used to contains variable or constructor def nodes only.
        /// </summary>
        List<TreeNode> priorityNode;

        internal SelectMembersForm()
        {
            InitializeComponent();
            CenterToScreen();
            priorityNode = new List<TreeNode>();
        }

        internal SelectMembersForm(CodeGenerator codeGenerator, ListOfOptionsForFirstWindow optionSelected)
            : this()
        {
            this.codeGenerator = codeGenerator;
            this.optionSelected = optionSelected;
            displayTreeMemberView();
        }

        /// <summary>
        /// This property sets the option selected in the first window.
        /// </summary>
        internal ListOfOptionsForFirstWindow OptionSelected
        {
            set
            {
                optionSelected = value;
            }
        }

        /// <summary>
        /// This Method is used to display the members of the tree
        /// </summary>
        private void displayTreeMemberView()
        {
            memberTreeView.CheckBoxes = true;
            TreeNode currentClassNode = new TreeNode(codeGenerator.GetThisName());
            currentClassNode.Expand();
            memberTreeView.Nodes.Add(currentClassNode);
            List<CodeVariable2> variables = codeGenerator.GetListOfVariables();
            if (variables != null)
            {
                foreach (CodeVariable2 temp in variables)
                {
                    currentClassNode.Nodes.Add(temp.Name);
                }
            }
            if (optionSelected == ListOfOptionsForFirstWindow.Constructor && codeGenerator.CodeClass != null)
            {
                List<CodeClass2> bases = codeGenerator.GetListOfBaseClasses();
                if (bases != null && bases.Count > 0)
                {
                    foreach (CodeClass2 baseClass in bases)
                    {
                        TreeNode baseNode = new TreeNode(baseClass.Name);
                        memberTreeView.Nodes.Add(baseNode);
                        baseNode.Expand();
                        List<CodeFunction2> constructors = CodeGenerator.GetListOfNonStaticConstructors(baseClass, null);
                        if (constructors != null && constructors.Count > 0)
                        {
                            foreach (CodeFunction2 constructor in constructors)
                            {
                                string constructorDefinition = getConstructorDef(constructor);
                                baseNode.Nodes.Add(constructorDefinition);
                            }
                        }
                    }
                }
            }

        }


        /// <summary>
        /// This method is used to handle the events during checking and unchecking of events.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void memberTreeView_AfterCheck(object sender, TreeViewEventArgs e)
        {
            if (e.Action == TreeViewAction.ByMouse)
            {
                errorLabel.Text = "";
                TreeNode node = e.Node;
                TreeNodeCollection childNodes = node.Nodes;
                if (node.Checked)
                {
                    if (checkIfCurrentClass(node))
                    {
                        if (childNodes != null && childNodes.Count > 0)
                        {
                            foreach (TreeNode childNode in childNodes)
                            {
                                childNode.Checked = true;
                                manipulateQueue(childNode, true);
                            }
                        }
                        else
                        {
                            manipulateQueue(node, true);
                        }

                    }
                    else
                    {
                        unCheckBaseClasseNVariables(node);
                    }

                }
                else
                {
                    if (childNodes != null && childNodes.Count > 0)
                    {
                        foreach (TreeNode childNode in childNodes)
                        {
                            childNode.Checked = false;
                            manipulateQueue(childNode, false);
                        }
                    }
                    else
                    {
                        //unckeck your parent
                        node.Parent.Checked = false;
                        manipulateQueue(node, false);
                    }
                }
            }

        }

        /// <summary>
        /// This method is used to uncheck all the other constructors except for selected constructor.
        /// </summary>
        /// <param name="node"></param>
        private void unCheckBaseClasseNVariables(TreeNode node)
        {
            List<CodeClass2> bases = codeGenerator.GetListOfBaseClasses();            
            if (checkIfNodeIsConstructor(node))
            {
                TreeNode parent = node.Parent;
                foreach (TreeNode parentTreeNode in memberTreeView.Nodes)
                {
                    //check if parent tree node is not a class node and parent tree node has some children
                    if ((!parentTreeNode.Text.Equals(codeGenerator.GetThisName())) && parentTreeNode.Nodes != null)
                    {
                        foreach (TreeNode constructorNode in parentTreeNode.Nodes)
                        {
                            if (!constructorNode.Text.Equals(node.Text))
                            {
                                if (constructorNode.Checked == true)
                                {                                    
                                    constructorNode.Checked = false;
                                    manipulateQueue(node, false);
                                }
                            }
                            else
                            {                                
                                manipulateQueue(node, true);
                            }

                        }
                    }
                }
            }
            else
            {
                errorLabel.Text = Constant.MSG_CANT_CHECK_BASE_CLASS;
                node.Checked = false;
            }
        }

        /// <summary>
        /// This Method is used to remove the node from its position.
        /// if isAdd is true then it adds it to the end of the list.
        /// </summary>
        /// <param name="node"></param>
        /// <param name="isAdd"></param>
        private void manipulateQueue(TreeNode node, bool isAdd)
        {
            if (node != null && (checkIfNodeIsVariable(node) || checkIfNodeIsConstructor(node)))
            {
                priorityNode.Remove(node);               
                if (isAdd)
                {
                    priorityNode.Add(node);                    
                }
            }
            
        }

        /// <summary>
        /// This Method is used to check if the node belongs to current class or base class.
        /// </summary>
        /// <param name="node"></param>
        /// <returns>true if node belongs to current class</returns>
        private bool checkIfCurrentClass(TreeNode node)
        {
            bool flag = false;
            if (node != null)
            {
                //if the node is child node.
                if (node.Parent != null && node.Parent.Text.Equals(codeGenerator.GetThisName()))
                {
                    // that means node is a variable and belongs to the class.
                    flag = true;
                }                
                else if (node.Text.Equals(codeGenerator.GetThisName()))
                {
                    //that means node is the class name
                    flag = true;
                }
            }
            return flag;
        }

        /// <summary>
        /// This Method is used to check if node is variable or not.
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private bool checkIfNodeIsVariable(TreeNode node)
        {
            bool flag = false;            
            //if the node is child node.
            if (node != null && node.Parent != null && node.Parent.Text.Equals(codeGenerator.GetThisName()))
            {
                // that means node is a variable and belongs to the class.
                flag = true;
            }
            return flag;
        }

        /// <summary>
        /// This method is used to check if the node is a constructor node or not.
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        private bool checkIfNodeIsConstructor(TreeNode node)
        {
            bool flag = false;
            //MessageBox.Show("check if NOde is constructor " + node.Text);
            if (node != null && node.Parent != null && (!node.Parent.Text.Equals(codeGenerator.GetThisName())))
            {
                // that means node is a variable and does not belong to the class.
                flag = true;
            }            
            return flag;
        }
               
        /// <summary>
        /// This method is used to get the access type which is 
        /// selected by using the radio buttons.
        /// </summary>
        /// <returns></returns>
        private string getAccess()
        {
            string access;
            if (publicRadioButton.Checked)
            {
                access = "public";
            }
            else if (internalRadioButton.Checked)
            {
                access = "internal";
            }
            else if (protectedRadioButton.Checked)
            {
                access = "protected";
            }
            else
            {
                access = "private";
            }

            return access;
        }

        /// <summary>
        /// This method is used to get the variables selected by the user.
        /// </summary>
        /// <returns></returns>
        private List<CodeVariable2> getVariablesSelected()
        {
            List<CodeVariable2> variablesChecked = null;
            List<CodeVariable2> allVariables = codeGenerator.GetListOfVariables();

            if (priorityNode.Count>0 && allVariables != null && allVariables.Count > 0)
            {
                variablesChecked = new List<CodeVariable2>();
                foreach (TreeNode childNode in priorityNode)
                {
                    if (checkIfNodeIsVariable(childNode))
                    {
                        foreach (CodeVariable2 variable in allVariables)
                        {
                            if (variable.Name.Equals(childNode.Text))
                            {
                                variablesChecked.Add(variable);
                                break;
                            }
                        }
                    }  
                }
            }

            return variablesChecked;
        }

        /// <summary>
        /// This Method is used to get the constructorSelected
        /// </summary>
        /// <returns></returns>
        private CodeFunction2 getConstructorSelected()
        {
            CodeFunction2 constructor=null;
            int positionOfConstructor = getConstructorPosition();
            TreeNode node = positionOfConstructor < priorityNode.Count ? priorityNode[positionOfConstructor] : null;            
            if (node != null)
            {
                List<CodeClass2> bases = codeGenerator.GetListOfBaseClasses();
                if (bases != null && bases.Count > 0)
                {
                    foreach (CodeClass2 baseClass in bases)
                    {
                        List<CodeFunction2> constructors = CodeGenerator.GetListOfNonStaticConstructors(baseClass, null);
                        if (constructors != null && constructors.Count > 0)
                        {
                            foreach (CodeFunction2 construct in constructors)
                            {
                                if (node.Text.Equals(getConstructorDef(construct)))
                                {
                                    constructor = construct;
                                    break;
                                }

                            }
                        }
                        if (constructor != null)
                        {
                            break;
                        }
                    }
                }
            }     
            return constructor;
        }

        /// <summary>
        /// This Method is used to get the position of the constructor in priority node
        /// returns priorityNode.Count if no constructor is present.
        /// </summary>
        /// <returns></returns>
        private int getConstructorPosition()
        {
            int position = priorityNode.Count;
            for (int i = 0; i < position; i++)
            {                
                if (checkIfNodeIsConstructor(priorityNode[i]))
                {
                    position = i;
                    break;
                }                
            }
            return position;
        }

        /// <summary>
        /// This Method is used to get the constructor definition without access and with type and parameters
        /// </summary>
        /// <param name="constructor"></param>
        /// <returns></returns>
        private string getConstructorDef(CodeFunction2 constructor)
        {
            string constructorDefinition = CodeGenerator.GetConstructorDefinition(constructor, false, true, true);
            return constructorDefinition;
        }

        /// <summary>
        /// This method is used to validate the  memebers form.
        /// </summary>
        /// <returns></returns>
        private bool validateForm()
        {
            bool flag = false;
            if (memberTreeView.Nodes != null)
            {
                foreach (TreeNode parentNode in memberTreeView.Nodes)
                {
                    if (parentNode.Checked == true)
                    {
                        flag=true;
                        break;
                    }
                    else
                    {
                        if (parentNode.Nodes != null)
                        {
                            foreach (TreeNode childNode in parentNode.Nodes)
                            {
                                if (childNode.Checked == true)
                                {
                                    flag = true;
                                    break;
                                }
                            }
                        }
                    }
                    if (flag)
                        break;
                }               
            }
            //if the optionselected is constructor then user can have empty constructor.
            if (!flag && optionSelected == ListOfOptionsForFirstWindow.Constructor)
            {
                flag = true;
            }
            return flag;
        }

        /// <summary>
        /// This Method is used to check if Constructor exits or not.
        /// </summary>
        /// <returns></returns>
        private bool checkIfConstructorExists()
        {
            bool flag = false;
            if (optionSelected == ListOfOptionsForFirstWindow.Constructor)
            {
                if (codeGenerator.CheckIfConstructorExists(getVariablesSelected(), getConstructorSelected(), getConstructorPosition()))
                {                   
                    flag = true;
                }
            }
            return flag;
        }

        private void finishButton_Click(object sender, EventArgs e)
        {
            if (!validateForm())
            {
                MessageBox.Show(Constant.MSG_CHECK_ATLEAST_ONE);
                return;
            }
            else if (checkIfConstructorExists())
            {
                MessageBox.Show(Constant.MSG_CONSTRUCTOR_EXISTS);
                return;
            }

            if (optionSelected == ListOfOptionsForFirstWindow.None)
            {
            }
            else if (optionSelected == ListOfOptionsForFirstWindow.Constructor)
            {                
                codeGenerator.AddConstructor(getVariablesSelected(), getConstructorSelected(),getConstructorPosition(), getAccess());
            }
            else
            {
                List<CodeVariable2> variablesChecked = getVariablesSelected();
                if (variablesChecked != null && variablesChecked.Count > 0)
                {
                    foreach (CodeVariable2 variable in variablesChecked)
                    {
                        codeGenerator.AddProperty(variable, getAccess(), optionSelected);
                    }
                }
            }
            this.Hide();
            this.Close();
            this.Dispose();          
        }

        private void cancelButton_Click(object sender, EventArgs e)
        {
            this.Hide();
            this.Close();
            this.Dispose();           
        }

        /// <summary>
        /// This method is used to display the previous options form.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backButton_Click(object sender, EventArgs e)
        {
            this.Hide();
            this.Close();
            this.Dispose();
            codeGenerator.ShowSelectOptionForm();
        }
    }
}
